import tkinter as tk
from tkinter import ttk, messagebox
from datetime import datetime
from protocol.codec import to_hex


def now_str() -> str:
    return datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]


class TcpLogs:
    def __init__(self, parent, resend_callback, send_to_decode_callback):
        self.parent = parent
        self.resend_callback = resend_callback
        self.send_to_decode_callback = send_to_decode_callback
        self.frame = ttk.LabelFrame(parent, text='TCP日志', padding=10)
        self.max_rows = 2000
        # 保存原始数据（避免把原始数据塞进Treeview的不可用选项）
        self._payloads_send = {}
        self._payloads_recv = {}

        self._build()

    def _build(self):
        notebook = ttk.Notebook(self.frame)
        notebook.pack(fill=tk.BOTH, expand=True)

        # 发送页
        self.send_tab = ttk.Frame(notebook)
        notebook.add(self.send_tab, text='发送记录')
        self._build_tab(self.send_tab, direction='Send')
        # 接收页
        self.recv_tab = ttk.Frame(notebook)
        notebook.add(self.recv_tab, text='接收记录')
        self._build_tab(self.recv_tab, direction='Recv', with_send_to_decode=True)

    def _build_tab(self, tab, direction: str, with_send_to_decode: bool = False):
        toolbar = ttk.Frame(tab)
        toolbar.pack(fill=tk.X)
        ttk.Button(toolbar, text='清空', command=lambda t=tab: self._clear_tab(t)).pack(side=tk.LEFT)
        if direction == 'Send':
            ttk.Button(toolbar, text='重发所选', command=self._resend_selected).pack(side=tk.LEFT, padx=(6, 0))
            ttk.Button(toolbar, text='送至解码面板并解析', command=self._send_send_to_decode).pack(side=tk.LEFT, padx=(6, 0))
        if with_send_to_decode:
            ttk.Button(toolbar, text='送至解码面板并解析', command=self._send_recv_to_decode).pack(side=tk.LEFT, padx=(6, 0))

        columns = ('time', 'cmd', 'summary', 'length')
        tree = ttk.Treeview(tab, columns=columns, show='headings')
        tree.pack(fill=tk.BOTH, expand=True)
        tree.heading('time', text='时间')
        tree.heading('cmd', text='指令ID')
        tree.heading('summary', text='缩略帧')
        tree.heading('length', text='长度')

        tree.column('time', width=170, anchor=tk.W)
        tree.column('cmd', width=80, anchor=tk.W)
        tree.column('summary', width=600, anchor=tk.W)
        tree.column('length', width=60, anchor=tk.E)

        tree.bind('<Double-1>', lambda e, t=tree: self._copy_selected(t))

        tab._tree = tree  # attach

    def _clear_tab(self, tab):
        tab._tree.delete(*tab._tree.get_children())

    def _copy_selected(self, tree):
        sel = tree.selection()
        if not sel:
            return
        iid = sel[0]
        # 优先复制完整原始数据（缩略仅用于视觉展示）
        payload = self._payloads_send.get(iid) or self._payloads_recv.get(iid)
        if payload is not None:
            text = to_hex(payload)
        else:
            vals = tree.item(iid, 'values')
            text = vals[2]
        tree.clipboard_clear()
        tree.clipboard_append(text)
        messagebox.showinfo('已复制', '已复制完整数据帧到剪贴板')

    def _resend_selected(self):
        tree = self.send_tab._tree
        sel = tree.selection()
        if not sel:
            messagebox.showwarning('提示', '请先选择一行')
            return
        iid = sel[0]
        data = self._payloads_send.get(iid)
        if not data:
            messagebox.showerror('错误', '未保存原始数据，无法重发')
            return
        self.resend_callback(data)

    def _send_send_to_decode(self):
        """发送记录的送至解码面板并解析"""
        sel = self.send_tab._tree.selection()
        if not sel:
            messagebox.showwarning('提示', '请先在发送记录中选择一行')
            return
        
        iid = sel[0]
        data = self._payloads_send.get(iid)
        if not data:
            messagebox.showerror('错误', '未找到发送记录的数据')
            return
            
        try:
            self.send_to_decode_callback(data)
        except Exception as e:
            messagebox.showerror('送至解码面板失败', f'无法将发送记录送至解码面板: {str(e)}')

    def _send_recv_to_decode(self):
        """接收记录的送至解码面板并解析"""
        sel = self.recv_tab._tree.selection()
        if not sel:
            messagebox.showwarning('提示', '请先在接收记录中选择一行')
            return
        
        iid = sel[0]
        data = self._payloads_recv.get(iid)
        if not data:
            messagebox.showerror('错误', '未找到接收记录的数据')
            return
            
        try:
            self.send_to_decode_callback(data)
        except Exception as e:
            messagebox.showerror('送至解码面板失败', f'无法将接收记录送至解码面板: {str(e)}')

    def _truncate_hex(self, b: bytes, width_chars: int) -> str:
        hx = to_hex(b)
        if len(hx) <= width_chars:
            return hx
        return hx[:max(0, width_chars - 3)] + '...'

    def _estimate_summary_width(self, tree) -> int:
        # 基于当前列宽动态计算可容纳字符数（粗估：每字符 ~ 7px）
        col_w = tree.column('summary', option='width')
        return max(16, int(col_w / 7))

    def append_send(self, cmd_id: str, data: bytes):
        tree = self.send_tab._tree
        width_chars = self._estimate_summary_width(tree)
        vals = (now_str(), cmd_id or '未知', self._truncate_hex(data, width_chars), len(data))
        # 倒序：插入到顶部
        iid = tree.insert('', 0, values=vals)
        # 存原始数据便于重发
        self._payloads_send[iid] = data
        self._trim_if_needed(tree)

    def append_recv(self, cmd_id: str, data: bytes):
        tree = self.recv_tab._tree
        width_chars = self._estimate_summary_width(tree)
        # 直接使用传入的cmd_id，已经格式化好了
        disp_cmd = cmd_id if cmd_id else '未知'
        vals = (now_str(), disp_cmd, self._truncate_hex(data, width_chars), len(data))
        # 倒序：插入到顶部
        iid = tree.insert('', 0, values=vals)
        self._payloads_recv[iid] = data
        self._trim_if_needed(tree)

    def _trim_if_needed(self, tree):
        total = len(tree.get_children())
        if total > self.max_rows:
            to_del = total - self.max_rows
            # 倒序显示，删除末尾的旧记录
            children = tree.get_children()
            for iid in children[-to_del:]:
                tree.delete(iid)


